<?php

/***  DOCUMENTATION LAYER

Ceo Framework Class

Name: CeoFramework
Last Update: Sep 2007
Author: Tom at klenwell@gmail.com

DESCRIPTION
  This is the main framework class for the Ceo framework.  It inherits the Ceo
  base.class.php

METHODS
  MAGIC
  CeoFramework($debug=0, $oid=null)    *php 4 constructor*
  __construct($debug, $oid)          *php 5 constructor*
  __destruct()  
  
  PUBLIC
  load_framework()
  load_ext($lib_ext)
  email($to_email, $to_name, $subject, $body)
  email_smtp($to_email, $to_name, $subject, $body)
  is_session_secure()
  set_ctrl_key()
  get_ctrl_key()
  
  PRIVATE
  _load_kernel()
  _security_gateway()
  _config_release_stage()
  _config_framework()
  _config_project()
  _define_constants()
  _authorize_host_server()
  _set_framework_error_handler()
  _alpha_error_handler($num, $msg, $file, $line)
  _beta_error_handler()
  _production_error_handler()
  _email_error()
  _get_project_base_path()
  _load_phpmailer()
  _set_session_data()
  _get_session_data()
  _has_session_data()
  _set_filename()
  
  
USAGE
  $Ceo = new CeoFramework();
  $Ceo->logging_on = 1;
  
  // having to call this explicitly gives us a chance to set some parameters
  $Ceo->load_framework();

NOTES


______________________________________________________________________________*/

// Load File of Base Class
$base_fname = 'base.class.php';
$base_dirpath = dirname(__FILE__) . DIRECTORY_SEPARATOR;
require_once($base_dirpath . $base_fname);


// Class
/*____________________________________________________________________________*/
class CeoFramework extends CeoBase
{
/* PUBLIC PROPERTIES */
var $debug = 0;
var $class_name = __CLASS__;
var $oid = '';
var $DS = DIRECTORY_SEPARATOR;

// encapsulated objects (see also base class properties)
var $Kernel = 0;
var $Mailer = 0;
var $Output = 0;

// public arrays
var $CEO = array();
var $PROJECT = array();             // global projects vars set in project driver file
var $VALID_ACK = array();           // valid Active Control Keys (sub-controller ids/file names)

// controller properties
var $mod_rewrite = 0;               // indicates where mod_rewrite is in effect determining how internal links are built
var $exec_file = 'index.php';       // file that is actually called to execute program -- used to build internal links
var $ack_id = 'ack';                // ack key id -- used in GET parameters (e.g. $_GET[$this->ack_id] -- see get_ctrl_key below)
var $default_ack = 'home';          // default control key (for sub-controller file)
var $ack = 'home';                  // active (sub)control key (one subctrl should be always active) -- this is session persisted
var $default_tpl = 'ceo';           // default ceo template file
var $tpl = '';                      // active template file assigned by active controller (e.g. 'tpl')
var $url_self = '';                 // framework equivalent of PHP_SELF -- adjusts for mod_rewrite setting             

// public vars
var $server_host = '';
var $language = 'en';
var $release_stage = '';
var $release_stage_verbose = '';
var $project_base_path = '';
var $url_root = '';
var $url_project_root = '';
var $mod_root = '';

// mail settings
var $use_smtp = 0;
var $smtp_host = '';
var $smtp_user = '';
var $smtp_password = '';
var $project_contact = '';

/* PRIVATE PROPERTIES */
var $_LOADED_EXT = array();   // extension tracking array


var $_filename = '';
var $_dirpath = '';


/* ** MAGIC METHODS ** */
// php4 constructor
function CeoFramework($debug=0, $oid=null)
{
  $this->__construct($debug, $oid);
  register_shutdown_function( array($this, '__destruct') );
}
// END constructor

// php5 constructor
function __construct($debug=0, $oid=null)
{
  // default
  $this->debug = $debug;
  $this->oid = ( empty($oid) ) ? $this->class_name : $oid;
  $this->_set_filename();
  $this->_set_dirpath();
  
  // global arrays
  global $PROJECT, $CEO;
  $this->PROJECT = $PROJECT;
  $this->CEO = $CEO;
  
  // call parent (loads Fso, Err objects)
  parent::__construct($debug, $oid);
  
  // set path and dir files
  $this->script_path = ( !empty($_SERVER['SCRIPT_FILENAME']) ) ? $_SERVER['SCRIPT_FILENAME'] : $_SERVER['PHP_SELF'];
  $this->php_self = $_SERVER['PHP_SELF'];
  $this->ceo_root = $this->_dirpath;
  $this->root = $this->ceo_root;
  $this->doc_root = $_SERVER['DOCUMENT_ROOT'] . $this->DS;
  $this->super_root = dirname($this->doc_root) . $this->DS;;
  $this->project_root = $this->CEO['project_root'] . $this->DS;
  $this->url_root = '/';
  $this->DIR['controller'] = $this->project_root . 'controller' . $this->DS;
  $this->DIR['output'] = $this->project_root . 'output' . $this->DS;
  $this->DIR['ext'] = $this->root . 'ext' . $this->DS;
  $this->DIR['ext_oss'] = $this->root . 'ext_oss' . $this->DS;
  $this->DIR['log_bin'] = $this->root . 'log_bin' . $this->DS;
  $this->DIR['phpmailer'] = $this->DIR['ext_oss'] . 'phpmailer' . $this->DS;
  $this->PATH['self'] = $this->php_self;
  $this->PATH['err_driver'] = $this->root . 'err.class.php';
  $this->PATH['fso_driver'] = $this->root . 'fso.class.php';
  $this->PATH['kernel_driver'] = $this->root . 'kernel.class.php';
  $this->PATH['mailer_driver'] = $this->DIR['phpmailer'] . 'class.phpmailer.php';
  $this->PATH['project_base'] = '';
  $this->PATH['datasafe'] = $this->super_root . 'datasafe'.$this->DS.'nudgewink.inc.php';
  $this->URL['root'] = $this->url_root;   
  $this->URL['project_root'] = $this->url_root;    // this will be reset when framework loaded
  $this->URL['self'] = $this->php_self;
  
  // release stage constant
  define('CEO_ALPHA', 1);
  define('CEO_BETA', 2);
  define('CEO_FINAL', 4);
  
  // framework properties
  $this->project_contact = $this->PROJECT['contact_email'];
  
  // add internal classes
  $this->_load_fso();
  $this->_load_err();
  $this->_load_phpmailer();
  
  // debug
  if ( $this->debug ) $this->print_d('debugging is active for oid ' . $this->oid);
  if ( $this->debug ) $this->print_d('constructor complete for class ' . __CLASS__);
}
// END constructor

// destructor
function __destruct()
{
  if ( $this->debug ) $this->print_d("destroying class {$this->class_name} (oid: {$this->oid})");
}
// END destructor



/* ** PUBLIC METHODS ** */
// method: load framework
function load_framework()
{
  if ( $this->debug ) $this->print_d('loading framework','green');
  $this->_load_kernel();
  $this->_security_gateway();
  $this->_config_release_stage();
  $this->_config_framework();
  $this->_config_project();
  $this->_define_constants();
  return;
}
// END method



// method: load extension
function load_ext($ext_id)
{
  // check tracking arrays
  if ( in_array($ext_id, $this->_LOADED_EXT) )
  {
    if ( $this->debug ) $this->print_d("ext id [$ext_id] already loaded", '#600');
    return;
  }
  
  // add to tracking array and build driver path
  $this->_LOADED_EXT[] = $ext_id;
  $ext_driver = $this->DIR['ext'] . $this->EXT[$ext_id] . $this->DS . 'driver.inc.php';
  if ( $this->debug ) $this->print_d("loading ext id [$ext_id] -> driver [$ext_driver]", '#060');
  
  if ( !is_file($ext_driver) )
  {
    $this->e("ext [$ext_id] driver [$ext_driver] not found", E_USER_ERROR);
    return 0;
  }
  
  require_once($ext_driver);
  return;
}
// END method



// method: load module
function load_mod($mod_path, $_ARG = null)
{
  $_MOD = array();  // reset MOD array

  /* Note on Module Return Array
    The return value should be an array consisting of these three values:
    
      $_MOD['Return'][0] : string - for display in browser    
      $_MOD['Return'][1] : boolean (0/1) - successful/without exception?
      $_MOD['Return'][2] : assoc. array - data (as a side-effect)
        
    This is a loose standard as this method simply loads the module and returns
    whatever is in the $_MOD['Return'] var without any integrity checks.  The
    module must self-police and/or the control file check the results on the
    back end.
    
    See file check block below for an example
  */
  $_MOD['Return'] = array( '', 0, null );

  // set mod parameters
  $_MOD['path'] = $mod_path;
  $_MOD['ARG'] = $_ARG;
  
  // argument check
  if ( !empty($_MOD['ARG']) && !is_array($_MOD['ARG']) )
  {
    $_MOD['ARG'] = array( $_MOD['ARG'] );
    $this->e('load module argument is scalar -- should be an array', E_USER_NOTICE);
  }
  
  // file check
  if ( !is_file($_MOD['path']) )
  {
    $this->e("module file [{$_MOD['path']}] not found", E_USER_WARNING);
    $_MOD['Return'] = array
    (
      '<h3>unable to load module</h3>',    
      0,
      null
    );
    return $_MOD['Return'];
  }
  
  // load and return
  require_once($_MOD['path']);
  return $_MOD['Return'];
}
// END method



// method: main controller (dispatch to active project controller)
function controller()
{
  // debug
  if ( $meta_debug = 0 )
  {
    $this->print_d('POST : ' . print_r($_POST,1));
    $this->print_d('GET : ' . print_r($_GET,1), 'orange');
    $this->print_d('SERVER : ' . print_r($_SERVER,1), 'olive');
  }
  
  // set framework url self value
  $_qsa = ( !empty($_SERVER['QUERY_STRING']) ) ? "?{$_SERVER['QUERY_STRING']}" : '';
  $this->url_self = ( isset($_SERVER['REDIRECT_URL']) ) ? 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REDIRECT_URL'] : $_SERVER['PHP_SELF'].$_qsa;
  
  // load active controller
  $this->_set_ack();
  $ack_path = CTRL_ROOT . $this->ack . '.ctrl.php';
  if ( $this->debug ) $this->print_d("calling active controller file [$ack_path]", 'turquoise');
  require_once($ack_path);
  
  return;
}
// END method



// method: dev
function output()
{
  global $_PAGE;    // make available to output file
  
  if ( empty($this->tpl) )
  {
    $this->e("ceo tpl property is empty -- will set to default [{$this->default_tpl}] and call that file", E_USER_WARNING);
    $this->tpl = $this->default_tpl;
  }
  
  $tpl_path = $this->DIR['output'] . $this->tpl . '.tpl.php';
  if ( $this->debug ) $this->print_d("call template file [$tpl_path]");
  require_once($tpl_path);
  return;
}
// END method



// method: dev
function postscript()
{
  global $_PAGE;
  if ( $this->debug ) $this->print_d("calling postscript");
  
  // postscript panel? if so, print
  if ( isset($this->Output->PANEL['postscript']) ) print $this->Output->PANEL['postscript'];
  
  // close html
  print "\n<!-- Close HTML -->\n</body>\n</html>\n";

  // flush buffer
  while (ob_get_level() > 0) { ob_end_flush(); }
  flush();
  print "<!-- buffer flushed at " . date('r') . " -->";

  // page time
  if ( isset($_PAGE['tx1']) )
  {
    $load_time = $this->tx_diff($_PAGE['tx1']);
    if ( isset($_PAGE['id']) ) $id = "[id: {$_PAGE['id']}] ";
    print "\n<!-- script {$id}generated in $load_time s -->\n";
  }

  return;
}
// END method




// method: redirect (to) url
function redirect_to_url($url, $code='303')
{
  if ( $this->debug ) $this->print_d("redirecting to url [$url] with code [$code]");
  
  // normalize url
  if ( !preg_match('%^(http)s?://%i', $url) )
  {
    $url = $this->Fso->normalize_slashes($url);
    if ( substr($url,0,1) == '/' ) $url = substr($url, 1);
    $url = $this->URL['root'] . $url;
  }
  
  // header sent?
  if ( headers_sent() )
  {
    $this->e('Redirect failed -> headers already sent', E_USER_WARNING);
    return FALSE;
  }
  
  // valid url?
  if ( !$this->is_valid_url_strict($url) ) 
  {
    $this->e("redirect failed -> invalid url: $url", E_USER_WARNING);
    return FALSE;
  }

  // set code
  // 301 code
  if ($code == '301') header("HTTP/1.1 301 Moved Permanently");	
  
  // 302 code
  elseif ($code == '302') header("HTTP/1.1 302 Found");
  
  // 303 code
  else header("HTTP/1.1 303 See Other");
    		
  // close session
  if ( session_id() ) { session_write_close(); }		

  // redirect
  header("Location: $url");
  
  // catch
  $catch = <<<HTML

<h3>We are now redirecting you to <a href="$url">$url</a></h3>
If you are not redirected, please click the link
  
HTML;

  die($catch);

  return;  
}
// END method



// method: err (trigger error)
function email($to_email, $to_name, $subject, $body)
{
  $is_sent = 0; // return
  
  $this->_reset_mailer = $this->Mailer->Mailer;

  if ( $this->use_smtp ) 
  {
    $this->_break_smtp_loop = 1;
    $this->email_smtp($to_email, $to_name, $subject, $body);
    $this->_break_smtp_loop = 0;
  }

  $this->Mailer->AddAddress($to_email, $to_name);
  $this->Mailer->Subject = $subject;
  $this->Mailer->Body    = $body;
  
  // send
  if( !$this->Mailer->Send() )
  {
    $_err = ( $this->Mailer->IsError() ) ? $this->Mailer->ErrorInfo : 'no error message';
    $this->e("unable to deliver email to $to_name [$to_email] : $_err", E_USER_WARNING);
    $is_sent = 0;
  }
  else
  {
    if ( $this->debug ) $this->print_d("message emailed to $to_email");
    $is_sent = 1;
  }
  
  // reset mailer
  $this->Mailer->Mailer = $this->_reset_mailer;
  
  return $is_sent;
}
// END method


// method: static mailer
function staticLoadMailer()
{
  $Object = 0;    // return
    
  global $CEO;            // framework global
  static $StaticMailer;   // static singleton class variable
  
  // classpath
  $_s = DIRECTORY_SEPARATOR;
  $_dir = dirname(__FILE__) . $_s . "ext_oss{$_s}phpmailer" . $_s;
  $_fname = 'class.phpmailer.php';
  
  // get instance of class
  if ( !is_object($StaticMailer) )
  {
    require_once($_dir . $_fname);
    $StaticMailer = new PHPMailer();
    $StaticMailer->From = ( !empty($PROJECT['contact_email']) ) ? $PROJECT['contact_email'] : 'noreply@' . $_SERVER['SERVER_NAME'];
    $StaticMailer->FromName = "CeoStaticMailer";
    if ( $debug ) CeoForm::print_d('StaticMailer created');
  }
  else
  {
    if ( $debug ) CeoForm::print_d('StaticMailer already exists', '#99f');
  }
  
  return $StaticMailer;
}
// END method


// method: load mailer object
function email_smtp($to_email, $to_name, $subject, $body)
{
  if ( empty($this->_break_smtp_loop) ) $this->_break_smtp_loop = 0;
  $this->_reset_mailer = $this->Mailer->Mailer;

  // make sure SMTP settings have been set
  if ( empty($this->smtp_host) ) { $this->e('must set `smtp_host` to use smtp', E_USER_WARNING); return 0; }
  if ( empty($this->smtp_user) ) { $this->e('must set `smtp_user` to use smtp', E_USER_WARNING); return 0; }
  if ( empty($this->smtp_password) ) { $this->e('must set `smtp_password` to use smtp', E_USER_WARNING); return 0; }  
  
  // set smtp settings
  $this->Mailer->Mailer = 'smtp';
  $this->Mailer->SMTPAuth = true;
  $this->Mailer->Host = $this->smtp_host;
  $this->Mailer->Username = $this->smtp_user;
  $this->Mailer->Password = $this->smtp_password;
  
  if ( $this->_break_smtp_loop ) return;
  
  return $this->email($to_email, $to_name, $subject, $body);
}
// END method


// method: load extension
function is_session_secure()
{
  $is_secure = 0;     // return
  if ( function_exists('session_regenerate_id') ) session_regenerate_id();
  if ( isset($_SESSION['fingerprint']) && ( $_SESSION['fingerprint'] == $this->Kernel->get_session_fingerprint() ) )
    $is_secure = 1;
  return $is_secure;
}
// END method


// method: dev
function shutdown($message, $title='default')
{
  if ( $title == 'default' ) $title = "{$this->server_host} :: unable to load page";
  $this->print_d(__FUNCTION__ . ' in dev');
  trigger_error("framework shutdown: $message", E_USER_ERROR);
  return;
}
// END method



// private: set (Session) Active Controller Key
function _set_sack($key)
{
  if ( $this->debug ) $this->print_d("setting ack (Active Controller Key) to $key");
  $this->ack = $key;
  $_SESSION[$this->oid][$this->ack_id] = $key;
  return;
}
// END method



// private: get ctrl key
function _set_ack()
{
  /* this sets a key which the ceo controller file then uses to load a
    sub-controller file.  priority is important here. */
    
  // sanity check
  if ( is_null($this->VALID_ACK) )
  {
    trigger_error('no valid active controller keys set -- set VALID_ACK before loading framework', E_USER_ERROR);
    return;
  }
    
  // 1. GET key
  if ( !empty($_GET[$this->ack_id]) ) 
  {
    if ( in_array($_GET[$this->ack_id], $this->VALID_ACK) )
    {
      $this->_set_sack($_GET[$this->ack_id]);
      return;
    }
  }
  
  // 2. POST key
  if ( !empty($_POST[$this->ack_id]) ) 
  {
    if ( in_array($_POST[$this->ack_id], $this->VALID_ACK) )
    {
      $this->_set_sack($_POST[$this->ack_id]);
      return;
    }
  }
  
  // 3. SESSION key
  if ( !empty($_SESSION[$this->oid][$this->ack_id]) )
  {
    if ( in_array($_SESSION[$this->oid][$this->ack_id], $this->VALID_ACK) )
    {
      $this->_set_sack($_SESSION[$this->oid][$this->ack_id]);
      return;
    }
  }
  
  // 4. default
  $req = "G|P|S -> {$_GET[$this->ack_id]}|{$_POST[$this->ack_id]}|{$_SESSION[$this->oid][$this->ack_id]}";
  trigger_error("ack [$req] empty or not valid, setting to default [{$this->default_ack}]", E_USER_WARNING);
  $this->_set_sack($this->default_ack);
  
  return;
}
// END method



// method: add post ctrl key
function add_post_ctrl_key($key)
{
  $this->print_d(__FUNCTION__ . ' in dev', 'black');
  $html = '<input type="hidden" name="'.$this->ckid.'" value="'.$key.'" />';
  return $html;
}
// END method



// method: validate url (may be called statically)
// based on Apeldoorn function: http://www.canowhoopass.com/guides/regex/validateurlsyntax_source.php
function is_valid_url_strict($url)
{
  $is_valid = 0;  // return
  
  $_RE['scheme'] = "((http://)|(https://)|(ftp://))?";
  $_RE['ip'] = "((((2(([0-4][0-9])|(5[0-5])))|([01]?[0-9]?[0-9]))\.){3}((2(([0-4][0-9])|(5[0-5])))|([01]?[0-9]?[0-9])))";
  $_RE['domain3'] = '([a-zA-Z0-9](([a-zA-Z0-9-]{0,62})[a-zA-Z0-9])?\.)*';
  $_RE['domain2'] = '([a-zA-Z0-9](([a-zA-Z0-9-]{0,62})[a-zA-Z0-9])?\.)';
  $_RE['domain1'] = '([a-zA-Z](([a-zA-Z0-9-]*)[a-zA-Z0-9])?)';
  $_RE['port'] = '(:(([0-5]?[0-9]{1,4})|(6[0-4][0-9]{3})|(65[0-4][0-9]{2})|(655[0-2][0-9])|(6553[0-5])))?';
  $_RE['path'] = '(/((;)?([a-zA-Z0-9_.!~*' . '\'' . '()-]|(%[0-9a-fA-F]{2})|' . '[:@&=+$,]' . ')+(/)?)*)?';
  $_RE['query'] = '(\?([;/?:@&=+$,]|[a-zA-Z0-9_.!~*' . '\'' . '()-]|(%[0-9a-fA-F]{2}))*)?';
  $_RE['anchor'] = '(#([;/?:@&=+$,]|[a-zA-Z0-9_.!~*' . '\'' . '()-]|(%[0-9a-fA-F]{2}))*)?';
  
  $_RE['address'] = '((' . $_RE['ip'] . ')|(' . $_RE['domain3'] . $_RE['domain2'] . $_RE['domain1'] . '))+';
  $_EREGI['url'] = '^' . $_RE['scheme'] . $_RE['address'] . $_RE['port'] . $_RE['path'] . $_RE['query'] . $_RE['anchor'] . '$';
  
  if ( eregi($_EREGI['url'], $url) ) $is_valid = 1;
  
  return $is_valid;
}
// END method



/* ** PRIVATE METHODS ** */
// method: load kernel
function _load_kernel()
{
  if ( $this->debug ) $this->print_d('loading kernel', 'blue');
  require_once($this->PATH['kernel_driver']);
  $this->Err->set_handler(array(&$this->Err,'kernel_handler'));
  $this->Kernel = new CeoKernel($this->debug, $this->oid.'_kernel');
  $this->Kernel->fix_globals();
  $this->Kernel->fix_magic_quotes();
  $this->Kernel->open_session();
  return;
}
// END method


// method: security checks
function _security_gateway()
{
  $admit = 0;     // return
  if ( $this->debug ) $this->print_d('security gateway', 'red');
  
  if ( !$this->_authorize_host_server() ) return 0;
  
  return 1;  
}
// END method


// method: security checks
function _config_release_stage()
{
  if ( $this->debug ) $this->print_d('configuring release stage', 'red');

  // set release stage
  if ( $this->release_stage_verbose == 'alpha' ) $this->release_stage = CEO_ALPHA;
  elseif ( $this->release_stage_verbose == 'beta' ) $this->release_stage = CEO_BETA;
  elseif ( $this->release_stage_verbose == 'final' ) $this->release_stage = CEO_FINAL;
  else
  {
    $this->release_stage_verbose = 'unidentified';
    $this->e("release stage unidentified: -> will set to CEO_FINAL", E_USER_WARNING);
    $this->release_stage = CEO_FINAL;
  }
  if ( $this->debug ) $this->print_d('release stage set to ' . $this->release_stage_verbose);

  // set project paths
  $this->project_base_path = $this->_get_project_base_path();
  
  // set project urls
  $this->url_root = 'http://' . $this->server_host . '/';
  $url_project_ext = ( !empty($this->project_base_path) ) ? $this->project_base_path . '/' : '';
  $this->url_project_root = CeoFso::normalize_slashes($this->url_root . $url_project_ext, '/');
  
  $this->URL['root'] = $this->url_root;
  $this->URL['project_root'] = $this->url_project_root;
  $this->URL['img_bin'] = $this->url_project_root . 'img_bin/';
  $this->URL['css_bin'] = $this->url_project_root . 'css_bin/';;
  
  // set error handler
  $this->_set_framework_error_handler();
  return; 
}
// END method


// method: config frame
function _config_framework()
{
  if ( $this->debug ) $this->print_d('configuring framework -- load default project config file');
  
  // load extension list and init extension tracking array
  require_once($this->ceo_root . "config/ext_list.inc.php");
  $this->_LOADED_EXT = array();
  
  // load default project config file
  require_once($this->ceo_root . "config/default.inc.php");
  
  // encapsulate output object
  $this->load_ext('out');
  $this->Output = new CeoOutput($this->debug, $this->oid.'_output');
  
  return;
}
// END method


// method: config project
function _config_project()
{
  if ( $this->debug ) $this->print_d('configuring project');
  
  // load project-specific config files
  if ( file_exists($this->project_root . 'config/ceo.project.inc.php') )
  {
    if ( $this->debug ) $this->print_d("project-specific config file found [{$this->project_root}config/ceo.project.inc.php]: loading now");
    require_once($this->project_root . "config/ceo.project.inc.php");
  }
  else
  {
    if ( $this->debug ) $this->print_d("no project-specific config file [{$this->project_root}config/ceo.project.inc.php] found: will not load");
  }
  
  // load release-specific config files
  if ( file_exists($this->project_root . 'config/ceo.{$this->release_stage_verbose}.inc.php') )
  {
    if ( $this->debug ) $this->print_d("release-specific config file found [{$this->project_root}config/ceo.{$this->release_stage_verbose}.inc.php]: loading now");
    require_once($this->project_root . 'config/ceo.{$this->release_stage_verbose}.inc.php');
  }
  else
  {
    if ( $this->debug ) $this->print_d("no release-specific config file [{$this->project_root}config/ceo.{$this->release_stage_verbose}.inc.php] found: will not load");
  }
    
  // load data safe (after local config file, in case we wish to set there)
  require_once($this->PATH['datasafe']);
  
  return;
}
// END method


// method: define constants
function _define_constants()
{
  if ( $this->debug ) $this->print_d('defining framework constants');
  define('RELEASE_STAGE', $this->release_stage);
  define('DOC_ROOT', $this->doc_root);
  define('SUPER_ROOT', $this->super_root);
  define('PROJECT_ROOT', $this->project_root);
  define('CEO_ROOT', $this->ceo_root);
  define('EXT_ROOT', $this->DIR['ext']);
  define('OSS_ROOT', $this->DIR['ext_oss']);
  define('CTRL_ROOT', $this->DIR['controller']);
  define('OUTPUT_ROOT', $this->DIR['output']);
  define('MOD_ROOT', $this->DIR['mod']);
  define('LOG_BIN', $this->DIR['log_bin']);
  define('URL_ROOT', $this->url_root);  
  define('URL_PROJECT_ROOT', $this->url_project_root);
  define('IMG_BIN', $this->URL['img_bin']);
  define('CSS_BIN', $this->URL['css_bin']);
  return;
}
// END method


// method: authenticate host server (source : http://shiflett.org/archive/211)
function _authorize_host_server()
{
  $host_found = 0;
  if ( $this->debug ) $this->print_d("check server name [{$_SERVER['SERVER_NAME']}] against project list: " . $this->print_r($this->PROJECT['SERVER_HOST']));
  
  foreach ( $this->PROJECT['SERVER_HOST'] as $_HOST )
  {
    if ( strtolower($_HOST[1]) == strtolower($_SERVER['SERVER_NAME']) )
    {
      $host_found = 1;
      $this->release_stage_verbose = $_HOST[0];
      $this->server_host = $_HOST[1];      
      break;
    }
  }
  
  // not found? kill.
  if ( !$host_found )
  {
    header('HTTP/1.0 400 Bad Request');
    $this->shutdown('unauthorized server name: ' . $_SERVER['SERVER_NAME']);
  }
  
  return $host_found;
}
// END method


// method: set framework error handler
function _set_framework_error_handler()
{
  if ( $this->debug ) $this->print_d("setting error handler for framework");
  $name = '';
  
  if ( $this->release_stage == CEO_ALPHA ) $name = '_alpha_error_handler';
  elseif ( $this->release_stage == CEO_BETA ) $name = '_beta_error_handler';
  elseif ( $this->release_stage == CEO_FINAL ) $name = '_production_error_handler';
  else 
  {
    $this->trigger("release stage [$stage] not recognized -- handler set to production", E_USER_WARNING);
    $name = 'production_handler';
  }
    
  $this->Err->set_handler(array(&$this,$name));  
  return;
}
// END method


// method: alpha error handler
function _alpha_error_handler($num, $msg, $file, $line)
{
  // uncomment this for redundant error signaling
  #if ( $this->debug ) $this->print_d('error [#'.$num.' : '.substr($msg,0,28).'] triggered -> handled by ' . __FUNCTION__);
  
  // collect data
  $this->Err->_collect_error_data($num, $msg, $file, $line);
  
  // filters

    // trivial
    if ( $this->Err->is_trivial )
    {
      $this->Err->_log_strict();
      if ( $this->Err->filter_trivial ) 
      {
        #if ( $this->debug ) $this->print_d("trival error: {$this->Err->summary} (#{$this->Err->strict_count})");
        return;
      }
    }
  
  // reporting    
  $this->Err->_display();
  $this->Err->_display_in_comment();
  $this->Err->_log();  
  if ( $this->Err->email_alpha ) $this->_email_error();
  
  return;
}
// END method


// method: framework beta error handler
function _beta_error_handler()
{
  $this->print_d(__FUNCTION__ . ' in dev');
  return;
}
// END method


// method: framework production error handler
function _production_error_handler()
{
  $this->print_d(__FUNCTION__ . ' in dev');
  return;
}
// END method


// method: dev
function _email_error()
{
  $is_sent = 0;   // return
  if ( $this->debug ) $this->print_d('attempting to email error');
  
  // request release
  if ( !$this->Err->_release_email() ) return 0;

  // prepare email
  $to_email = $this->project_contact;
  $to_name = $this->PROJECT['name'] . ' Admin';
  $subject = "{$this->PROJECT['name']} Error: *{$this->Err->type}* on {$this->Err->date}";
  
  // dump data and purge passwords
  $dump_data = $this->Err->print_r($this);
  $dump_data = $this->_purge_passwords($dump_data);
  
  $body = <<<TEXT
  
ERROR REPORT

ERROR SUMMARY
{$this->Err->summary}

OBJECT DUMP
$dump_data

TEXT;

  // append error log
  $this->print_d('append error log to Err email');
 
  // send email
  if ( $is_sent = $this->email($to_email, $to_name, $subject, $body) )
  {
    if ( $this->debug ) $this->print_d("error email has been sent to project contact [$to_email]");
    $this->Err->_script_emails_sent++;
    $_SESSION[$this->Err->_sid]['email_count']++;
    $this->Err->_write_timelog();
  }
  else
  {
    if ( $this->debug ) $this->print_d('failed to send error email', 'magenta');
  }  
  
  return $is_sent;
}
// END method


// method: get project base path (diff doc root and ceo project driver root)
function _get_project_base_path()
{
  $base_path = '';

  if ( strpos($this->project_root, $this->doc_root) == 0 )
  {
    $base_path = substr($this->project_root, strlen($this->doc_root)); 
  }
  
  return $base_path;
}
// END method


// method: load mailer object
function _load_phpmailer()
{
  // already loaded
  if ( is_object($this->Mailer) ) return;
  
  require_once($this->PATH['mailer_driver']);
  
  // create mailer object
  $this->Mailer = new PHPMailer();
  $this->Mailer->SetLanguage($this->language, $this->DIR['phpmailer'] . 'language' . $this->DS);
  $this->Mailer->From = ( !empty($this->project_contact) ) ? $this->project_contact : trigger_error('project email contact ($this->project_contact) not set', E_USER_WARNING);
  $this->Mailer->FromName = "CeoMailer";
  
  return;
}
// END method


// method: _set_session_data
function _set_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
  $_SESSION[$this->oid] = array();
  
  // add session data here
  
  return;
}
// END method

// method: get session data
function _get_session_data()
{
  // initialize session
  if ( !$this->_has_session_data() ) return; 
    
  // retrieve session variables
  // $this->var = $_SESSION[$this->oid]['var'];
  
  return;
}
// END method

// method: has session data
function _has_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
    
  // retrieve session variables
  if ( empty($_SESSION[$this->oid]) ) return 0;
  else return 1;
}
// END method

function _set_filename() { $this->_filename = basename(__FILE__); }
function _set_dirpath() { $this->_dirpath = dirname(__FILE__) . $this->DS; }

} // end class
/*____________________________________________________________________________*/

?>
